package com.zlp.mall.portal.modules.oms.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.Date;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zlp.mall.common.api.Pager;
import com.zlp.mall.common.api.RespCode;
import com.zlp.mall.common.exception.ApiException;
import com.zlp.mall.common.exception.CustomException;
import com.zlp.mall.common.redis.RedisService;
import com.zlp.mall.common.util.BeanToUtils;
import com.zlp.mall.common.util.DecimalUtils;
import com.zlp.mall.common.util.MethodUtil;
import com.zlp.mall.common.util.RedisConst;
import com.zlp.mall.portal.component.trade.alipay.TradePayProp;
import com.zlp.mall.portal.config.SessionMap;
import com.zlp.mall.portal.modules.oms.dto.req.OrderSubmitRep;
import com.zlp.mall.portal.modules.oms.dto.resp.*;
import com.zlp.mall.portal.modules.oms.mapper.OrderMapper;
import com.zlp.mall.portal.modules.oms.model.CartItem;
import com.zlp.mall.portal.modules.oms.model.Order;
import com.zlp.mall.portal.modules.oms.model.OrderItem;
import com.zlp.mall.portal.modules.oms.model.OrderSetting;
import com.zlp.mall.portal.modules.oms.service.CartItemService;
import com.zlp.mall.portal.modules.oms.service.OrderItemService;
import com.zlp.mall.portal.modules.oms.service.OrderService;
import com.zlp.mall.portal.modules.oms.service.OrderSettingService;
import com.zlp.mall.portal.modules.pms.model.SkuStock;
import com.zlp.mall.portal.modules.pms.service.SkuStockService;
import com.zlp.mall.portal.modules.ums.model.Member;
import com.zlp.mall.portal.modules.ums.model.MemberReceiveAddress;
import com.zlp.mall.portal.modules.ums.service.MemberReceiveAddressService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author ZouLiPing
 * @since 2021-10-15
 */
@Service
@Slf4j(topic = "OrderServiceImpl")
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    private  CartItemService cartItemService;
    @Resource
    private MemberReceiveAddressService memberReceiveAddressService;
    @Resource
    private SessionMap sessionMap;
    @Resource
    private OrderSettingService orderSettingService;
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private RedisService redisService;
    @Resource
    private SkuStockService skuStockService;
    @Resource
    private TradePayProp tradePayProp;



    @Override
    public OrderConfrimResp generateConfirmOrder(List<Long> ids) {

        log.info("generateConfirmOrder.req ids={}",ids);
        OrderConfrimResp orderConfrimResp = new OrderConfrimResp();

        // 商品信息
        List<CartItem> cartItemList = cartItemService.listByIds(ids);
        List<CarItemResp> cartList = BeanToUtils.entityToList(cartItemList, CarItemResp.class);
        orderConfrimResp.setCartList(cartList);

        // 收货地址信息
        Member sessionUser = sessionMap.getSessionUser();
        LambdaQueryWrapper<MemberReceiveAddress> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MemberReceiveAddress::getMemberId,sessionUser.getId());
        List<MemberReceiveAddress> addressList = memberReceiveAddressService.list();
        orderConfrimResp.setAddressList(addressList);

        // 获取商品价格
        getCartPrice(orderConfrimResp,cartList);

        return orderConfrimResp;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long generateOrder(OrderSubmitRep orderSubmitRep) {

        log.info("generateOrder.req orderSubmitRep={}", JSON.toJSONString(orderSubmitRep));
        Member sessionUser = sessionMap.getSessionUser();

        // 商品信息
        List<Long> itemIds = orderSubmitRep.getItemIds();
        OrderConfrimResp orderConfrimResp = new OrderConfrimResp();
        // 校验购物ids
        LambdaQueryWrapper<CartItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CartItem::getMemberId,sessionUser.getId());
        queryWrapper.in(CartItem::getId,itemIds);
        queryWrapper.orderByDesc(CartItem::getId);
        List<CarItemResp> cartList = cartItemService.cartList(queryWrapper);

        // 校验库存
        String productNmae = checkStok(cartList);
        if (StrUtil.isNotBlank(productNmae)) {
            throw new CustomException(RespCode.UNKONW.getCode(),String.format("亲您的手速过慢,该商品[%s]已经抢购完了",productNmae),MethodUtil.getLineInfo());
        }

        getCartPrice(orderConfrimResp,cartList);

        // 添加订单
        Order order = addOrder(orderSubmitRep, sessionUser, orderConfrimResp);
        // 添加订单明细
        addOrderItem(cartList, order);
        // 锁定库存
        lockStock(cartList);
        // 删除购物车
        delCart(cartList);
        return order.getId();
    }

    @Override
    public OrderDetailResp orderDetail(Long orderId) {

        log.info("orderDetail.req orderId={}",orderId);
        Order order = getByOrderId(orderId);
        OrderDetailResp orderDetailResp = new OrderDetailResp();
        orderDetailResp.setId(orderId);
        orderDetailResp.setReceiverProvince(order.getReceiverProvince());
        orderDetailResp.setReceiverCity(order.getReceiverCity());
        orderDetailResp.setReceiverRegion(order.getReceiverRegion());
        orderDetailResp.setReceiverDetailAddress(order.getReceiverDetailAddress());
        orderDetailResp.setPayAmount(order.getPayAmount());
        orderDetailResp.setOrderSn(order.getOrderSn());
        List<OrderItem> orderItemList = orderItemService.getByOrderId(orderId);
        orderDetailResp.setOrderItemList(orderItemList);
        orderDetailResp.setCreateTime(order.getCreateTime());
        orderDetailResp.setStatus(order.getStatus());

        OrderSetting orderSetting = getOrderSetting();
        orderDetailResp.setNormalOrderOvertime(orderSetting.getNormalOrderOvertime());
        return orderDetailResp;
    }

    @Override
    public Order getByOrderId(Long orderId) {

        Order order = this.getById(orderId);
        if (Objects.isNull(order)) {
            throw new CustomException(RespCode.FRONT_PMS_4002,MethodUtil.getLineInfo());
        }
        return order;
    }

    @Override
    public void cancelOverTimeOrder() {

        // 1.获取规定的时间
        OrderSetting orderSetting = orderSettingService.getById(1L);
        // 普通订单的超时分钟
        Integer overtime = orderSetting.getNormalOrderOvertime();

        // 获取当前时间的指定时间之前
        DateTime offset = DateUtil.offset(new Date(), DateField.MINUTE, -overtime);

        // 2. 获取超过规定时间未支付的订单
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Order::getStatus,0)  //未支付
                .le(Order::getCreateTime,offset);  //是否超时


        // 所有超时未支付的订单
        List<Order> list = this.list(queryWrapper);
        if(CollectionUtil.isEmpty(list)){
            log.warn("暂无超时订单");
            return;
        }
        // 订单的id 用于获取订单详情
        List<Long> orderIds=new ArrayList<>();
        for (Order order : list) {
            order.setStatus(4); // 设置订单关闭
            order.setModifyTime(new Date());
            orderIds.add(order.getId());
        }

        // 3. 改变状态：取消
        this.updateBatchById(list);


        // 4. 归还锁定库存
        // 4.1 获取订单详情
        QueryWrapper<OrderItem> itemQueryWrapper = new QueryWrapper<>();
        itemQueryWrapper.lambda().in(OrderItem::getOrderId,orderIds);
        List<OrderItem> itemList = orderItemService.list(itemQueryWrapper);// in (订单id)
        // 循环归还库存
        for (OrderItem omsOrderItem : itemList) {
            // 归还的数量
            Integer productQuantity = omsOrderItem.getProductQuantity();
            // skuid
            Long productSkuId = omsOrderItem.getProductSkuId();
            UpdateWrapper<SkuStock> stockUpdateWrapper = new UpdateWrapper<>();
            stockUpdateWrapper.setSql("lock_stock=lock_stock-"+productQuantity)
                    .lambda()
                    .eq(SkuStock::getId,productSkuId);
            skuStockService.update(stockUpdateWrapper);
        }
    }

    @Override
    public Pager<OrderListResp> getMyOrders(Integer pageSize, Integer pageNum) {

        log.info("getMyOrders.req pageSize={},pageNum={}",pageSize,pageNum);
        Page<Order> page = new Page<>(pageNum,pageSize);
        Long memberId = sessionMap.getSessionUser().getId();
        LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Order::getMemberId,memberId);
        queryWrapper.orderByDesc(Order::getId);
        Page<Order> pageData = this.page(page, queryWrapper);
        if (Objects.nonNull(pageData) && CollectionUtil.isNotEmpty(pageData.getRecords())) {
            List<Order> records = pageData.getRecords();
            List<OrderListResp> orderListResps = BeanToUtils.entityToList(records, OrderListResp.class);
            for (OrderListResp orderListResp : orderListResps) {
                orderListResp.setOrderItemList(orderItemService.getByOrderId(orderListResp.getId()));
            }
            return new Pager(orderListResps,pageData.getTotal(),pageSize,pageNum);
        }
        return new Pager<>();
    }

    @Override
    public OrderItemDetailDTO getOrderItemDetail(Long orderId) {

        log.info("getOrderItemDetail.req orderId={}",orderId);
        OrderItemDetailDTO orderItemDetailDTO;
        Order order = this.getById(orderId);
        orderItemDetailDTO = BeanToUtils.doToDto(order, OrderItemDetailDTO.class);

        List<OrderItem> orderItems = orderItemService.getByOrderId(order.getId());
        orderItemDetailDTO.setOrderItemList(orderItems);
        return orderItemDetailDTO;
    }

    /** 
     * <p>
     *      1.更新订单状态和支付方式、支付时间    唯一标识
     *      2.清除锁定库存，扣除实际库存
     *      3.删除二维码
     *
     * </p>
     * @param orderId
     * @param payType
     * @date: 2021/10/25 15:03
     * @return: void 
     */
    @Override
    public void paySuccess(Long orderId, Integer payType) {

        log.info("paySuccess.req orderId={},payType={}",orderId,payType);
        Order omsOrder=new Order();
        omsOrder.setStatus(1); // 待发货
        omsOrder.setPayType(payType);
        omsOrder.setPaymentTime(new Date());
        omsOrder.setId(orderId);

        UpdateWrapper<Order> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(Order::getStatus,omsOrder.getStatus())
                .set(Order::getPayType,omsOrder.getPayType())
                .set(Order::getPaymentTime,omsOrder.getPaymentTime())
                .eq(Order::getId,omsOrder.getId());
        boolean update = this.update(updateWrapper);
        if(!update){
            throw  new ApiException("订单支付成功：更新失败！");
        }

        //清除锁定库存，扣除实际库存
        QueryWrapper<OrderItem> itemQueryWrapper = new QueryWrapper<>();
        itemQueryWrapper.lambda().eq(OrderItem::getOrderId,omsOrder.getId());
        List<OrderItem> list = orderItemService.list(itemQueryWrapper);
        // 所有需要更新库存的id
        for (OrderItem omsOrderItem : list) {
            UpdateWrapper<SkuStock> skuUpdateWrapper = new UpdateWrapper<>();
            skuUpdateWrapper.setSql("stock=stock-"+omsOrderItem.getProductQuantity())
                    .setSql("lock_stock=lock_stock-"+omsOrderItem.getProductQuantity())
                    .lambda().eq(SkuStock::getId,omsOrderItem.getProductSkuId());
            skuStockService.update(skuUpdateWrapper);
        }

        // 需要修改为运行机器上的路径
        String fileName= String.format("/qr-%s.png", orderId);
        String filePath = tradePayProp.getStorePath()+fileName;
        // 如果二维码存在
        if(FileUtil.exist(filePath) && FileUtil.isFile(filePath)){
            // 删除二维码
            FileUtil.del(filePath);
        }
    }

    private Order addOrder(OrderSubmitRep orderSubmitRep, Member sessionUser, OrderConfrimResp orderConfrimResp) {
        Order order = new Order();
        order.setMemberId(sessionUser.getId());
        order.setOrderSn(generateOrderSn(0));
        order.setMemberUsername(sessionUser.getUsername());

        order.setTotalAmount(orderConfrimResp.getPriceTotal());
        order.setPayAmount(orderConfrimResp.getPayAmount());
        order.setFreightAmount(orderConfrimResp.getFreightAmount());
        order.setSourceType(0);
        order.setStatus(0);
        order.setOrderType(0);
        // 订单设置
        OrderSetting orderSetting = getOrderSetting();
        order.setAutoConfirmDay(orderSetting.getConfirmOvertime());
        Long memberReceiveAddressId = orderSubmitRep.getMemberReceiveAddressId();
        // 添加收货地址信息
        addReceiveAddress(order, memberReceiveAddressId);
        order.setDeleteStatus(0);
        order.setCreateTime(DateUtil.date());
        order.setModifyTime(new Date());
        this.save(order);
        return order;
    }

    /** 
     * 获取订单设置
     * @date: 2021/10/22 17:53
     * @return: com.zlp.mall.portal.modules.oms.model.OrderSetting 
     */
    private OrderSetting getOrderSetting() {
        return orderSettingService.getOne(new LambdaQueryWrapper<>());
    }

    /**
     * 添加订单明细
     * @param cartList
     * @param order 
     * @since: 2021/10/21 21:25
     * @return: void
     */
    private void addOrderItem(List<CarItemResp> cartList, Order order) {
        Collection<OrderItem> orderItemList = new ArrayList<>();
        for (CarItemResp carItemResp : cartList) {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(order.getId());
            orderItem.setOrderSn(order.getOrderSn());
            orderItem.setProductId(carItemResp.getProductId());
            orderItem.setProductPic(carItemResp.getProductPic());
            orderItem.setProductName(carItemResp.getProductName());
            orderItem.setProductBrand(carItemResp.getProductBrand());
            orderItem.setProductSn(order.getOrderSn());
            orderItem.setProductPrice(carItemResp.getPrice());
            orderItem.setProductQuantity(carItemResp.getQuantity());
            orderItem.setProductSkuId(carItemResp.getProductSkuId());
            orderItem.setProductSkuCode(carItemResp.getProductSkuCode());
            orderItem.setProductCategoryId(carItemResp.getProductCategoryId());
            orderItem.setPromotionAmount(new BigDecimal("0"));
            orderItem.setCouponAmount(new BigDecimal("0"));
            orderItem.setIntegrationAmount(new BigDecimal("0"));
            orderItem.setRealAmount(carItemResp.getPrice());
            orderItem.setGiftIntegration(0);
            orderItem.setGiftGrowth(0);
            orderItem.setProductAttr(carItemResp.getProductAttr());
            orderItemList.add(orderItem);
        }
        orderItemService.saveBatch(orderItemList);
    }

    /**
     * 添加收货地址信息
     * @param order
     * @param memberReceiveAddressId 
     * @date: 2021/10/21 21:24
     * @return: void
     */
    private void addReceiveAddress(Order order, Long memberReceiveAddressId) {
        MemberReceiveAddress memberReceiveAddress = memberReceiveAddressService.getByMemberReceiveAddressId(memberReceiveAddressId);
        order.setReceiverName(memberReceiveAddress.getName());
        order.setReceiverPhone(memberReceiveAddress.getPhoneNumber());
        order.setReceiverPostCode(memberReceiveAddress.getPostCode());
        order.setReceiverProvince(memberReceiveAddress.getProvince());
        order.setReceiverCity(memberReceiveAddress.getCity());
        order.setReceiverRegion(memberReceiveAddress.getRegion());
        order.setReceiverDetailAddress(memberReceiveAddress.getDetailAddress());
    }

    /**
     *  删除购物车
     * @param cartItemList
     * @date: 2021/10/21 21:22
     * @return: void
     */
    private void delCart(List<CarItemResp> cartItemList) {

        List<Long> carIds = cartItemList.stream().map(CarItemResp::getId).collect(Collectors.toList());
        cartItemService.removeByIds(carIds);
    }

    /**
     * 校验库存
     * @param cartList
     * @date: 2021/10/21 21:22
     * @return: java.lang.String
     */
    private String checkStok(List<CarItemResp> cartList) {
        String productName = null;
        for (CarItemResp carItemResp : cartList) {
            SkuStock skuStock = skuStockService.getBySkuId(carItemResp.getProductSkuId());
            if (skuStock.getStock() < carItemResp.getStock()) {
                productName = carItemResp.getProductName();
                break;
            }
        }
        return productName;
    }

    /** 
     * 锁定库存
     * @param cartList
     * @date: 2021/10/21 17:56
     * @return: void 
     */
    private void lockStock(List<CarItemResp> cartList) {

        for (CarItemResp cartItem : cartList) {
            Integer quantity = cartItem.getQuantity();
            Long productSkuId = cartItem.getProductSkuId();
            SkuStock skuStock = new SkuStock();
            skuStock.setId(productSkuId);
            skuStock.setLockStock(quantity);
            skuStockService.updateById(skuStock);
        }
    }

    /** 
     * 生成订单号
     * @param sourceType 订单类型
     * @date: 2021/10/19 9:47
     * @return: java.lang.String 
     */
    private String generateOrderSn(int sourceType) {

        // 年月日 8位
        SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMdd");
        String yyyymmdd = sdf.format(DateUtil.date());
        StringBuilder sb = new StringBuilder(yyyymmdd);
        // 订单类型占2位,不足补0
        String orderType = String.format("%02d", sourceType);
        sb.append(orderType);
        // 生成自增6,不足补0
        Long seq = redisService.incr(RedisConst.ORDER_SEQ, 1);
        String format = String.format("%07d", seq);
        sb.append(format);
        return sb.toString();
    }



    private void getCartPrice(OrderConfrimResp orderConfrimResp, List<CarItemResp> cartList) {

        Integer productTotal = 0;
        BigDecimal priceTotal = BigDecimal.ZERO;
        BigDecimal payAmount;
        BigDecimal freightAmount = BigDecimal.ZERO;
        for (CarItemResp cart : cartList) {
            productTotal += cart.getQuantity();
            priceTotal = DecimalUtils.add(priceTotal,DecimalUtils.multiply(cart.getPrice(),DecimalUtils.toBigDecimal(cart.getQuantity())));
        }
        payAmount = DecimalUtils.add(priceTotal,freightAmount);
        orderConfrimResp.setProductTotal(productTotal);
        orderConfrimResp.setPriceTotal(priceTotal);
        orderConfrimResp.setFreightAmount(freightAmount);
        orderConfrimResp.setPayAmount(payAmount);
    }
}
